package node.func.param;

import grammar.Lexer;
import grammar.MyError;
import lexical.Key;
import midcode.MidCode;
import midcode.MidcodeGenerator;
import midcode.OPType;
import node.Node;
import node.exp.Exp;
import symbol.Function;
import symbol.Symbol;
import symbol.SymbolTable;

import java.util.ArrayList;
import java.util.Objects;

public class FuncRParams implements Node {
    private final Lexer lexer;
    private final SymbolTable table;
    private final Node father;
    private final Function function;
    private final int identLine;
    private ArrayList<Symbol> RParams = new ArrayList<>();
    private ArrayList<Exp> exps = new ArrayList<>();

    public FuncRParams(Lexer lexer, SymbolTable table, Node father, Function function, int identLine) {
        this.lexer = lexer;
        this.table = table;
        this.father = father;
        this.function = function;
        this.identLine = identLine;
        parseFuncRParams();
    }

    public void parseFuncRParams() {
        RParams.add(getRParam());
        exps.add(new Exp(lexer, table, this));
        while (lexer.isWordType(0, Key.COMMA)) {
            lexer.getCurWord(); // ,
            RParams.add(getRParam());
            exps.add(new Exp(lexer, table, this));
        }
        if (function.parameters.size() != RParams.size()) {
            lexer.addError(new MyError(identLine, 'd'));
        } else if (!haveSameParams()) {
            lexer.addError(new MyError(identLine, 'e'));
        }
    }

    public boolean haveSameParams() {
        for (int i = 0; i < RParams.size(); i++) {
            if (RParams.get(i).dim != -999 && RParams.get(i).dim != function.parameters.get(i).dim) {
                return false;
            }
        }
        return true;
    }

    public Symbol getRParam() {
        int i = 0;
        while (lexer.isWordType(i, Key.LPARENT)) {
            i++;
        }
        if (lexer.isWordType(i, Key.IDENFR)) {
            int dim = 0;
            Function function = table.getFunction(lexer.getWord(i).word);
            if (function != null) {
                dim = (Objects.equals(function.returnType, "void")) ? -1 : 0;
                return new Symbol(function.returnType, dim, function.ident, table.getSymbolId());
            }
            Symbol symbol = table.getSymbolByWord(lexer.getWord(i).word);
            if (symbol == null) {
                return new Symbol("int", -999, lexer.getWord(i), table.getSymbolId());
            }
            while (!lexer.isWordType(i, Key.COMMA) && !lexer.isWordType(i, Key.RPARENT) && !lexer.isWordType(i, Key.SEMICN)) {
                i++;
                if (lexer.isWordType(i, Key.LBRACK)) {
                    dim++;
                }
                if (lexer.isWordType(i, Key.PLUS) || lexer.isWordType(i, Key.MINU) || lexer.isWordType(i, Key.MULT) || lexer.isWordType(i, Key.DIV)) {
                    return new Symbol("int", 0, symbol.word, table.getSymbolId());
                }
            }
            dim = symbol.dim - dim;
            return new Symbol("int", dim, symbol.word, table.getSymbolId());
        } else {
            return new Symbol("int", 0, lexer.getWord(i), table.getSymbolId());
        }
    }

    @Override
    public Node getFather() {
        return father;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(exps.get(0).toString());
        for (int i = 1; i < exps.size(); i++) {
            sb.append("COMMA ,").append("\n");
            sb.append(exps.get(i).toString());
        }
        return sb.append("<FuncRParams>\n").toString();
    }

    @Override
    public String genMidCode() {
        MidcodeGenerator mg = MidcodeGenerator.getInstance();
        int cnt = 0;
        for (Exp exp : exps) {
            mg.addMidcode(new MidCode(OPType.PUSH_PARAM, exp.genMidCode(), Integer.toString(cnt++), function.getName()));
        }
        return "";
    }
}

